import { Injectable } from '@nestjs/common'
import { transformListToTree } from '@/utils'
import { InjectRepository } from '@nestjs/typeorm'
import { Equal, FindOptionsWhere, Like, Not, Repository } from 'typeorm'
import { ApiException, CreateMenuDTO, MenuEntity, UpdateMenuDTO } from '@/common'

@Injectable()
export class MenuService {
  constructor(@InjectRepository(MenuEntity) private readonly menuRepository: Repository<MenuEntity>) {}

  /** 新增菜单 */
  public async create(createDto: CreateMenuDTO): Promise<string> {
    if (createDto.type === 'M' || createDto.type === 'C') {
      const menu = await this.menuRepository.findOneBy({ path: Equal(createDto.path) })
      if (menu) throw new ApiException(`路由 ${createDto.path} 已存在`)
    } else if (createDto.type === 'F') {
      const permission = await this.menuRepository.findOneBy({ permission: Equal(createDto.permission) })
      if (permission) throw new ApiException(`按钮权限 ${createDto.permission} 已存在`)
    }

    this._purgeActionDto(createDto)
    await this.menuRepository.save(createDto)
    return '添加成功'
  }

  /** 根据菜单 ID 删除单条数据 */
  public async deleteOneById(menuId: string): Promise<string> {
    await this.menuRepository.findOneBy({ id: Equal(menuId) })
    const children = await this.menuRepository.findBy({ parentId: Equal(menuId) })
    if (children && children.length) throw new ApiException('该菜单下还存在其它菜单，无法删除')
    await this.menuRepository.delete(menuId)
    return '删除成功'
  }

  /** 更新菜单 */
  public async update(updateDto: UpdateMenuDTO) {
    this._purgeActionDto(updateDto)
    await this.menuRepository.save(updateDto)
    return '更新成功'
  }

  /** 移除新增编辑数据上的多余字段 */
  private _purgeActionDto(dto: CreateMenuDTO | UpdateMenuDTO): void {
    const delKeyList: string[] = []
    if (dto.type === 'M' || dto.type === 'C') delKeyList.push('permission')
    if (dto.type === 'F') delKeyList.push('icon', 'component', 'path')
    delKeyList.forEach((key) => delete dto[key])
    if (dto.full === 1 && dto.parentId !== '0') throw new ApiException('非一级菜单禁止设置一级路由')
  }

  /** 查询菜单列表 */
  public async findList(queryParams: Partial<MenuEntity> = {}) {
    const { status, type, title } = queryParams
    const where: FindOptionsWhere<MenuEntity> = {}
    if (title) where.title = Like(`%${title}%`)
    if (type) where.type = Equal(type)
    if (status) where.status = Equal(+status)
    const records = await this.menuRepository.find({ where, order: { order: 'ASC' } })
    return records
  }

  /** 根据 id 查询单条菜单数据 */
  public async findOneById(menuId: string): Promise<MenuEntity> {
    const where: FindOptionsWhere<MenuEntity> = { id: Equal(menuId) }
    const menu = await this.menuRepository.findOne({ where })
    if (!menu) throw new ApiException('菜单不存在或已被删除')
    return menu
  }

  /** 查询父级菜单下拉列表 */
  public async findParentList() {
    const where: FindOptionsWhere<MenuEntity> = { status: Equal(1), type: Not('F') } // 查询条件
    const records = await this.menuRepository.find({ where, order: { order: 'ASC' } })
    return [{ parentId: '0', id: '0', title: '主类目', children: transformListToTree(records) }]
  }

  /** 根据菜单 id 组查询所属菜单及按钮权限 */
  public async findManyByIds(menuIds: string[]) {
    const queryBuilder = this.menuRepository.createQueryBuilder('menu')
    queryBuilder.where('menu.id IN (:...menuIds)', { menuIds })
    queryBuilder.andWhere('menu.status = 1')
    queryBuilder.orderBy('menu.order', 'ASC')
    return queryBuilder.getMany()
  }

  /** 根据角色 id 组获取其所有的菜单权限 */
  public async findRoutesByRoleIds(roleIds: string[], isAdmin: boolean) {
    if (isAdmin) {
      return await this.menuRepository
        .createQueryBuilder('menu')
        .andWhere('menu.type <> :type', { type: 'F' }) // 只查询不是 F 类型的菜单
        .distinct(true) // 去重
        .orderBy('menu.order', 'ASC') // 按 order 升序
        .getMany()
    }
    if (!isAdmin) {
      return await this.menuRepository
        .createQueryBuilder('menu')
        .innerJoin('menu.roles', 'role')
        .where('role.id IN (:...roleIds)', { roleIds }) // 根据所属角色 id 组查询
        .andWhere('menu.type <> :type', { type: 'F' }) // 只查询不是 F 类型的菜单
        .distinct(true) // 去重
        .orderBy('menu.order', 'ASC') // 按 order 升序
        .getMany()
    }
  }

  /** 根据角色 id 组获取其所有的按钮权限 */
  public async findPermissionsByRoleIds(roleIds: string[], isAdmin: boolean) {
    // 管理员角色返回所有的权限编码组
    if (isAdmin) {
      const permissionMenuList = await this.menuRepository.find({ where: { type: Equal('F') } })
      return permissionMenuList.map((menu) => menu.permission)
    }
    // 非管理员角色则根据其所属角色查询
    const queryBuilder = this.menuRepository.createQueryBuilder('menu')
    queryBuilder.innerJoin('menu.roles', 'role')
    queryBuilder.where('role.id IN (:...roleIds)', { roleIds }) // 根据所属角色 id 组查询
    queryBuilder.andWhere('menu.type = :type', { type: 'F' }) // 只查询按钮类别的菜单
    queryBuilder.distinct(true) // 去重
    const permissionMenuList = await queryBuilder.getMany()
    return permissionMenuList.map((menu) => menu.permission)
  }
}
